grammar Cymbol;

compilationUnit // [编译单元]
    :   (classDeclaration | methodDeclaration | varDeclaration)+
    ;

classDeclaration // [类定义]
    :   'class' ID superClass? '{' classMember+ '}' ';'
    ;
    
superClass // [父类]  // todo: 这个有必要单独成一个节点吗？  // 不行，因为我们需要访问 idToken 去获取行号等必要信息
    :   ':' ID
    ;

classMember
    :   varDeclaration    
    |   methodDeclaration 
    ;   

// START: method
methodDeclaration   // [方法定义]
    :   type ID '(' formalParameters? ')' block
    ;
// END: method

formalParameters  // [参数列表]
    :   parameter (',' parameter)*
    ;

parameter
	:	type ID         #varParam       // [变量定义]
	|	type ID '[]'    #arrayParam     // [指针定义]  数组化归为指针
	|	type '*' ID     #pointerParam   // [指针定义]
	;

type // [类型引用]
    :	primitiveType
    |	ID
	;

primitiveType
	:   'float'
    |   'int'
    |	'char'
    |	'boolean'
    |	'void'
    ;

// START: block
block // [代码块]
    :   '{' statement* '}'
    ;
// END: block

// START: var
varDeclaration
    :   type ID ('=' expression)? ';'       #varDecl       // [变量定义]
    |   type ID '[]' ('=' expression)? ';'  #arrayDecl     // [指针定义]  数组化归为指针
    |   type '*' ID ('=' expression)? ';'   #pointerDecl   // [指针定义]
    ;
// END: var

statement
    :   block                                                 #blockStatement   // 直接Visit // todo: 试下直接visit的不重写可以吗
    |	varDeclaration                                        #varStatement     // 直接Visit
    |   'if' '(' condition=expression ')' trueStatement=statement 
        ('else' falseStatement=statement)?                    #ifStatement      // [条件语句]
    |   'return' expression? ';'                              #returnStatement  // [返回语句]
    |   unaryExpression '=' expression ';'                    #assignStatement  // [赋值语句]
    |   postfixExpression ';'                                 #callStatement    // 直接Visit. handles function calls like f(i);
    ;

expressionList // [表达式列表]
    :   expression (',' expression)*
    ;

expression
    :   postfixExpression
    |   unaryExpression
    |   binaryExpression
    ;

binaryExpression
    :   arithmeticExpression
    |   relationalExpression
    |   equalityExpression
    ;

arithmeticExpression // [算术表达式]
    :  arithmeticExpression op='*' arithmeticExpression
    |  arithmeticExpression op='/' arithmeticExpression
    |  arithmeticExpression op='+' arithmeticExpression
    |  arithmeticExpression op='-' arithmeticExpression
    |  unaryExpression
    ;

relationalExpression // [关系表达式]
    :  relationalExpression op='<'  relationalExpression
    |  relationalExpression op='>'  relationalExpression
    |  relationalExpression op='<=' relationalExpression
    |  relationalExpression op='>=' relationalExpression
    |  unaryExpression
    ;

equalityExpression // [匹值表达式]
    :   equalityExpression op='!=' equalityExpression
    |   equalityExpression op='==' equalityExpression
    |   unaryExpression
    ;

unaryExpression
    :   '-' unaryExpression   #minusExpression        // [取负表达式]
    |   '!' unaryExpression   #notExpression          // [取反表达式]
    |   '&' unaryExpression   #addressExpression      // [取地址表达式]
    |   '*' unaryExpression   #dereferenceExpression  // [解引用表达式]
    |   postfixExpression     #callExpression         // 直接Visit
    ;

// START: call
postfixExpression   // 后缀表达式类
    :   primary
    |   postfixExpression postfix
    ;
// END: call

primary
    :   ID
    |   INT
    |	FLOAT
    |	CHAR
    |	BOOL
    |   '(' expression ')'      // 直接visit
    ;

postfix
    :   '(' expressionList? ')'     #methodCall         // [方法调用表达式]
    |   '[' expression ']'          #arrayAccess        // 转换为  a[i] -> *(a + i)
    |   '.' ID                      #memberAccess       // [成员访问表达式]
    |   '->' ID                     #pointerAccess      // 转换为  a->b -> (*a).b
    ;


// LEXER RULES

BOOL
    :   'true'
    |   'false'
    ;

ID  :   LETTER (LETTER | '0'..'9')*
    ;

fragment
LETTER  :   ('a'..'z' | 'A'..'Z')
    ;

CHAR:	'\'' . '\'' ;

INT :   '0'..'9'+
    ;

FLOAT
	:	INT '.' INT*
	|	'.' INT+
	;

WS  :   (' '|'\r'|'\t'|'\n') -> channel(HIDDEN) // 要保留空白符
    ;

SL_COMMENT
    :   '//' ~('\r'|'\n')* '\r'? '\n' -> channel(HIDDEN) // 要保留注释
    ;
